home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-10-13 | 18.1 KB | 497 lines | [TEXT/MPS ] |
- (******************************************************************************
- *
- * Apple Macintosh Developer Technical Support
- *
- * Code for the printing routines
- *
- * Program: Sample 3.0
- * FILE: Print.inc1.p - Pascal implementation
- *
- * by: Matt Deatherage
- *
- * Copyright © 1988-1993 Apple Computer, Inc.
- * All rights reserved.
- *
- *******************************************************************************
-
- (*******************************************************************************
- * Types
- *******************************************************************************)
-
- TYPE
-
- (*******************************************************************************
- * HeightAndWidth is a record containing page counts -- how many pieces of paper
- * do we have to use to make this entire document print? We print in a
- * rectangular array, and this record tells us how many pages wide and tall
- * that array has to be.
- *******************************************************************************)
-
- HeightAndWidth = RECORD
- height: INTEGER;
- width: INTEGER;
- END;
-
- (*******************************************************************************
- * Private global variables maintained by this unit. These variables are
- * used across printing functions and are here because, while they're not
- * the business of code outside this unit, they also don't need to be passed
- * as parameters to every routine in here.
- *******************************************************************************)
-
- VAR
- theStatus: TPrStatus; { a Status record for our printing loop }
- pageDims: HeightAndWidth; { dimensions of the page we're using }
- documentPicture: PicHandle; { a PICT of what we're printing }
-
- (*******************************************************************************
- * This routine is referenced here so that we can unload its segments without
- * having to USE the entire UNIT it's contained in.
- *******************************************************************************)
-
- FUNCTION DoCircleOptions(VAR circle: CircleRec): BOOLEAN;
- EXTERNAL;
-
- {$S Print}
- (******************************************************************************
- *
- * private: GetPages
- *
- * GetPages takes a rectangle representing the size of what we're trying to
- * print, and a print record (which tells us how big the paper we've got is),
- * and returns a HeightAndWidth record which tells us how many pages it
- * takes to print this document.
- *
- * We calculate how many pixels wide (or tall) the document is, then how
- * many pixels wide (or tall) the imageable area is. Dividing the document
- * dimension by the page dimension shows how many pages (in that direction)
- * the document requires, incremented if there's any remainder indicating
- * more stuff on the next page.
- *
- ******************************************************************************)
-
- FUNCTION GetPages(theDocBounds: Rect; thePrintRecord: THPrint): HeightAndWidth;
-
- VAR
- pageHeight, { the height of the imageable paper area }
- docHeight, { the height of the document we're printing }
- pageWidth, { the width of the imageable paper area }
- docWidth, { the width of the document we're printing }
- height, { how many pages across this document takes }
- width: INTEGER; { how many pages down this document takes }
-
- BEGIN
- WITH theDocBounds DO
- docWidth := right - left; { width of the document }
- WITH thePrintRecord^^.prInfo.rPage DO
- pageWidth := right - left; { width of the imageable area }
-
- width := docWidth DIV pageWidth; { Whole # of pages required }
- IF docWidth MOD pageWidth <> 0 THEN { If there's any left over, then }
- width := width + 1; { add another page to hold it }
-
- WITH theDocBounds DO
- docHeight := bottom - top; { height of the document }
- WITH thePrintRecord^^.prInfo.rPage DO
- pageHeight := bottom - top; { height of the document }
-
- height := docHeight DIV pageHeight; { Whole # of pages required }
- IF docHeight MOD pageHeight <> 0 THEN { If there's any left over, then }
- height := height + 1; { add another page to hold it }
-
- GetPages.width := width; { fill in the record to return }
- GetPages.height := height; { the values we just calculated }
-
- END; { GetPages }
-
- {$S Print}
- (******************************************************************************
- *
- * private: DetermineRealNumberOfPagesInDoc
- *
- * This routine is part of Luke's Technical Note, and returns an INTEGER
- * saying how many sheets of paper we require. We're passed a rectangle
- * indicating the size of the document or Picture we're printing, and a print
- * record. We pass those through to GetPages, and return the height
- * multiplied by the width. It would be easy enough to fold this routine
- * and GetPages together, but this division more closely follows the
- * routines as defined in the Technical Note.
- *
- ******************************************************************************)
-
- FUNCTION DetermineRealNumberOfPagesInDoc(documentSize: Rect;
- printRecord: THPrint): INTEGER;
-
- BEGIN
- pageDims := GetPages(documentSize, printRecord);
- DetermineRealNumberOfPagesInDoc := pageDims.height * pageDims.width;
- END;
-
- {$S Print}
- (******************************************************************************
- *
- * private: PostPrintingErrors
- *
- * This procedure alerts the user that something went wrong during printing.
- * As long as the error isn't "The user canceled printing," we use NumToString
- * and ParamText to set up an ASCII version of the number as "^0". We
- * then call our utility routine AlertUser, which presents the error to the
- * user.
- *
- ******************************************************************************)
-
- PROCEDURE PostPrintingErrors(theError: OSErr);
-
- VAR
- theErrorString: Str255; { String to hold ASCII number. Must be
- Str255 because it's a formal VAR
- parameter. }
-
- BEGIN
- IF theError <> iPrAbort THEN { make sure the user didn't cancel }
- BEGIN
- NumToString(LONGINT(theError), theErrorString);
- ParamText(theErrorString, '', '', '');
- AlertUser(rPrintingError);
- END;
- END; { PostPrintingErrors }
-
- {$S Print}
- (******************************************************************************
- *
- * private: DrawStuff
- *
- * in some applications, this would be a call to the same routine that draws
- * your windows. in this application, we always print a QuickDraw PICT so
- * we leave this routine private.
- *
- * DrawStuff draws page number pageNumber of thePicture. thePage lets us
- * offset the Picture so we can draw the right portion of a multi-page PICT.
- * We set the port to thePort before drawing.
- *
- * The offsetting algorithm isn't obvious; let's look at it a bit. First,
- * the original Picture frame (the size of the whole Picture) is copied into
- * the offsetPage rectangle. To make DrawPicture draw the Picture offset
- * onto the page we want, but without scaling it, we keep the same rectangle
- * size but move it. If we want to draw the second page across, we need
- * to shift the rectangle of the Picture left by one page width, so the
- * second page is the part starting at (0,0).
- *
- *
- * We arbitrarily decide to draw pages horizontally first and vertically
- * second, so now we can calculate a formula for offsetting. Here's a
- * picture of a 12-page PICT we might be printing to help illustrate:
- *
- * +-----+-----+-----+-----+
- * | | | | |
- * | 1 | 2 | 3 | 4 |
- * | | | | |
- * +-----+-----+-----+-----+
- * | | | | |
- * | 5 | 6 | 7 | 8 |
- * | | | | |
- * +-----+-----+-----+-----+
- * | | | | |
- * | 9 | 10 | 11 | 12 |
- * | | | | |
- * +-----+-----+-----+-----+
- *
- * We calculate the amount to move the rectangle by taking the page number
- * we're printing and dividing by the number of pages across in our page
- * dimensions (HeightAndWidth record), using the remainder. In this example,
- * we're printing page #6. Dividing 6 by 4 and taking the remainder gives
- * 2, giving us the second page in the row. Multiplying that by the width
- * of a page gives us the amount to offset horizontally. Almost.
- *
- * The trick is that the first page in each row doesn't need to be offset
- * horizontally (and the first page in each column doesn't need to be offset
- * vertically), and the fourth page needs to be offset by three pages, not
- * zero. so before doing the division, we subtract one from the page number
- * to account for this. Multiplying the result by the page width gives
- * the amount to offset, but since we need to move the picture left or up,
- * the offset is negative. That gives this formula for horizontal offset:
- *
- * -((pageNumber - 1) MOD pageDims.width) * (thePage.right - thePage.left)
- *
- * and the vertical offset counterpart:
- *
- * -((pageNumber - 1) DIV pageDims.width) * (thePage.bottom - thePage.top)
- *
- * The vertical direction uses DIV and the width to give the row number, much
- * as MOD and the width gives the column number.
- *
- ******************************************************************************)
-
- PROCEDURE DrawStuff(thePage: Rect; thePort: GrafPtr; pageNumber: INTEGER;
- thePicture: PicHandle);
-
- VAR
- offsetPage: Rect; { copy of Picture frame rectangle }
-
- BEGIN
-
- pageNumber := pageNumber - 1; { to simplify calculations }
- offsetPage := thePicture^^.picFrame;
- OffsetRect(offsetPage,
- -((pageNumber) MOD pageDims.width) * (thePage.right - thePage.left),
- -((pageNumber) DIV pageDims.width) * (thePage.bottom - thePage.top));
- SetPort(thePort);
- DrawPicture(thePicture, offsetPage);
-
- END; { DrawStuff }
-
- {$S Print}
- (******************************************************************************
- *
- * Public: PageSetup
- *
- * PageSetup opens the Printing Manager and, if things are OK, presents the
- * standard style dialog if OKToInteract (in SampleUtilities) says that it's
- * OK to talk to the user.
- *
- * Although Sample doesn't require this, this routine (from the Technical Note)
- * preserves the current GrafPort because the Printing Manager may change it.
- *
- * Errors are not posted until the Printing Manager is closed. More on this
- * is in the description for the Print routine.
- *
- ******************************************************************************)
-
- FUNCTION PageSetup(thePrRecHdl: THPrint): BOOLEAN;
-
- VAR
- printError: OSErr; { any error that may be returned }
- oldPort: GrafPtr; { the GrafPort upon entry }
- oldResFile: INTEGER; { the Printing Manager's res file }
-
- BEGIN
- GetPort(oldPort); { save the current GrafPort }
- oldResFile := CurResFile; { save the resource file because the
- Printing Manager changes it }
- IF thePrRecHdl <> NIL THEN
- BEGIN
- PrOpen;
- IF (PrError = noErr) THEN
- BEGIN
- IF OKToInteract THEN
- IF PrStlDialog(thePrRecHdl) THEN
-
- { Do any post-processing on the style dialog results
- you need to here }
-
- ELSE
- PrSetError(iPrAbort); { if we can't do the
- dialog, indicate that
- we aborted it }
- END;
- printError := PrError;
-
- PrClose;
-
- IF (printError <> noErr) AND (printError <> iPrAbort) THEN
- PostPrintingErrors(printError);
- END;
-
- UseResFile(oldResFile); { restore the resource file }
- SetPort(oldPort); { restore the GrafPort }
-
- END; {PageSetup}
-
- {$S Print}
- (******************************************************************************
- *
- * Public: Print
- *
- * This routine implements the printing loop. We always print PICTs in this
- * code, so we'll accept a PICT handle directly if theWindow is NIL.
- *
- * Print first saves the old resource file because the Printing Manager
- * changes it. Then it gets a PICT of the document (or uses the one passed)
- * and counts the number of pages in it using DetermineNumberOfPagesInDoc.
- *
- * After that, we update all our application windows so they don't have holes
- * in them while we print, and then we call PrJobDialog if OKToInteract
- * says interaction is allowed. (We go ahead and print if we can't call the
- * job dialog to be Apple Event friendly.) However, if we're passed a merge
- * print record, we call NewPrJobMerge (from Macintosh Technical Note "Fun
- * with PrJobMerge") to merge those parameters instead of asking the user
- * for new ones.
- *
- * With all the pleasantries aside, we're ready to go. We get the number of
- * copies from the print record, and the requested first and last pages of
- * the document. We then normalize those values and open an optional printing
- * status dialog. Sample doesn't actually _have_ one of these, but as long
- * as it doesn't require any events all you have to do to get one is add 'DLOG'
- * #257 to the resource fork and it will magically appear. If it needs event
- * handling, you'll have to add a pIdleProcedure to handle events in it.
- *
- * Then, for each requested copy, switch to the saved resource file, open
- * a document with PrOpenDoc, set the port to the returned port, open each
- * page and draw it. We back out gracefully through each call.
- *
- * Note that we do _not_ check for errors in the printing loop -- you must
- * call each PrCloseXXX routine for every PrOpenXXX routine you call. If you
- * call PrOpenPage, you _must_ call PrClosePage even if PrOpenPage returns
- * an error. So we Handle errors at the end.
- *
- * After it's all looped through, we call PrPicFile if necessary, post
- * any printing errors and dispose of the optional status dialog if we have one.
- *
- ******************************************************************************)
-
- FUNCTION Print(thePrRecHdl: THPrint; theWindow: WindowPtr; thePict: PicHandle;
- theMergeRec: THPrint): BOOLEAN;
-
- VAR
- copies, { count variable for copies loop }
- firstPage, { first page to print as requested }
- lastPage, { last page to print as requested }
- numberOfCopies, { how many copies to print }
- pageNumber, { count variable for pages loop }
- printMgrsResFile, { saved Printing Mgr's res file }
- realNumberOfPagesInDoc: INTEGER; { normalized count of pages to print }
- printError: OSErr; { errors as returned by Printing Mgr }
- oldPort: GrafPtr; { GrafPort on entry to routine }
- thePrPort: TPPrPort; { returned by PrOpenDoc }
- printingStatusDialog: DialogPtr; { optional Status dialog Ptr }
- documentBounds: Rect; { size of entire Picture/document }
- canWePrint, { TRUE if printing should proceed }
- ignore: BOOLEAN; { Functions must return values in Pascal,
- but we don't have to use them }
-
- BEGIN
- Print := FALSE; { assume we didn't print }
- GetPort(oldPort); { From the Technical Note -- save port }
- printingStatusDialog := NIL; { indicate no status dialog }
-
- IF (MemError = noErr) AND (thePrRecHdl <> NIL) THEN
- BEGIN
- UnloadSeg(@DoCircleOptions); { unload the dialogs segment }
- PrOpen;
- IF (PrError = noErr) THEN
- BEGIN
- printMgrsResFile := CurResFile; { save the resource file }
-
- { if we're passed a WindowPtr, make a Picture from
- that window's contents. Otherwise, use the Picture
- we're passed. }
-
- IF theWindow <> NIL THEN
- documentPicture := MakeDocumentPicture(DocumentPtr(GetWRefCon
- (theWindow)))
- ELSE
- documentPicture := thePict;
- documentBounds := documentPicture^^.picFrame;
- realNumberOfPagesInDoc :=
- DetermineRealNumberOfPagesInDoc(documentBounds,
- thePrRecHdl);
-
- UpdateAllAppWindows;
-
- canWePrint := TRUE;
-
- { If we have a merge record, use it and PrJobMerge instead of
- asking the user for more job values }
-
- IF theMergeRec = NIL THEN
- BEGIN
- IF OKToInteract THEN
- canWePrint := PrJobDialog(thePrRecHdl);
- END
- ELSE
- BEGIN
- ignore := PrValidate(thePrRecHdl);
- NewPrJobMerge(theMergeRec, thePrRecHdl);
- END;
-
- IF canWePrint THEN
- BEGIN
- numberOfCopies := thePrRecHdl^^.prJob.iCopies;
-
- { If we leave the page range as it is in the dialog, the
- Printing Manager will expect us to draw all pages, and it
- will throw away the unnecessary ones. For example, if the
- user picks pages 7-12, the Printing Manager expects us to
- print at least pages 1-12, and it will throw away pages
- 1-6. kind of ineffcient. Instead, we take the page range
- out of the print record and reset the page range to the
- defaults, and we start printing with (say) page 7. That
- way we only draw what needs to be printed. }
-
- firstPage := thePrRecHdl^^.prJob.iFstPage;
- lastPage := thePrRecHdl^^.prJob.iLstPage;
-
- thePrRecHdl^^.prJob.iFstPage := iPrPgFst;
- thePrRecHdl^^.prJob.iLstPage := iPrPgMax;
-
- IF (realNumberOfPagesInDoc < lastPage) THEN
- lastPage := realNumberOfPagesInDoc;
-
- IF OKToInteract THEN
- printingStatusDialog := GetNewDialog(257,NIL, POINTER( - 1));
-
- FOR copies := 1 TO numberOfCopies DO
- BEGIN
-
- { If you have a pIdleProcedure, install it here like this:
-
- thePrRecHdl^^.prJob.pIdleProc := @MyPIdleProcedure; }
- UseResFile(printMgrsResFile); { restore the driver's res file}
- thePrPort := PrOpenDoc(thePrRecHdl, NIL, NIL);
- { open a document with PrOpenDoc }
- SetPort(GrafPtr(thePrPort)); { switch to the printing GrafPort }
-
- IF (PrError = noErr) THEN
- BEGIN { print the requested range of pages }
- pageNumber := firstPage;
- WHILE ((pageNumber <= lastPage) AND (PrError = noErr)) DO
- BEGIN
-
- PrOpenPage(thePrPort, NIL);
- { open each page with no scaling rect }
-
- IF (PrError = noErr) THEN
- DrawStuff(thePrRecHdl^^.prInfo. rPage, GrafPtr(thePrPort),
- pageNumber,documentPicture);
- PrClosePage(thePrPort);
- pageNumber :=pageNumber + 1;
- END;
- PrCloseDoc(thePrPort);
- END;
- END;
- END
- ELSE { or else the user cancelled }
- PrSetError(iPrAbort);
- END;
-
- { call PrPicFile if necessary }
-
- IF (thePrRecHdl^^.prJob.bJDocLoop = bSpoolLoop) AND
- (PrError = noErr) THEN
- PrPicFile(thePrRecHdl, NIL, NIL, NIL, theStatus);
-
- printError := PrError; { Grab this before closing the Printing
- Manager, when it vanishes }
-
- PrClose;
-
- { NOW we can safely report errors }
-
- IF (printError <> noErr) THEN
- PostPrintingErrors(printError)
- ELSE
- Print := TRUE;
-
- { if we made a document Picture, we should kill it }
-
- IF (theWindow <> NIL) THEN
- KillPicture(documentPicture);
-
- END;
-
- IF (printingStatusDialog <> NIL) THEN { if there's a Status dialog }
- DisposeDialog(printingStatusDialog); { then dispose of it }
-
- SetPort(oldPort); { restore the original GrafPort }
- END; { Print }
-